package cn.tedu.knows.faq.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

// 我们的Aop在Spring框架完成
// 所以当前类对象必须保存在Spring容器中
@Component
// 下面的注解表示当前类是一个编写切面设计的类
// 专门编写aop代码,不编写一般业务
@Aspect
public class DemoAspect {

    // 1.定义切面
    // 定义切面就是指定一个方法
    // 我们可以在这个方法运行之前或之后等特殊的位置插入额外代码
    @Pointcut("execution(public * cn.tedu.knows.faq.controller.TagController.tags(..))")
    // 这个注解后必须要跟一个方法
    // 这个方法名称就是这个切面的名称 单纯的一个定义,不需要写任何代码
    public void pointCut() {
    }

    // 2.织入内容
    // 向切面指定的方法前后添加额外代码
    // 设置前置增强,在tags方法运行前先输出指定内容
    @Before("pointCut()")
    public void before(JoinPoint joinPoint) {
        // 在方法运行之前输出测试内容
        System.out.println("前置通知执行");
        // 方法参数JoinPoint包含当前切面的各种信息
        // 其实可以理解为方法的反射
        System.out.println(joinPoint.getSignature().getName());
    }

    //后置通知
    @After("pointCut()")
    public void after(){
        System.out.println("后置通知执行");
    }

    // 异常通知(只有在目标方法发生异常时才执行)
    @AfterThrowing("pointCut()")
    public void throwing(){
        System.out.println("方法发生异常");
    }

    // 环绕通知
    @Around("pointCut()")
    // 环绕通知比较特殊,必须编写ProceedingJoinPoint类型的参数
    // 这个参数的功能是能够调用我们的目标方法
    // 方法要有返回值,因为我们要解释目标方法的返回值以便返回给它的调用者
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        //ProceedingJoinPoint 是JoinPoint的子类型,实际上是接口
        // JoinPoint不能调用目标方法运行的功能
        System.out.println("环绕通知,运行之前");
        // 调用目标方法并接收返回值
        Object obj=joinPoint.proceed();
        System.out.println("环绕通知,运行之后");
        return obj;
    }
}
